/*
 usage:compile this module by make
       load module by command "sudo insmod tasklet_irq.ko irqn=1 devname=tasklet_irq"
       irqn:irq number;devname:device name.
       irqn=1:interrupt share with i8042 keyboard controller(PS/2 port)
              usb keyboard use lsusb and cat /proc/interrupts to find.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>

static int irqn;
static char* devname;
static struct tasklet_struct slam_tl;

module_param(irqn,int,0644);
module_param(devname,charp,0644);

static void slam_tl_handler(unsigned long data)
{
	printk("Hello slam tasklet handler!\n");
}

static irqreturn_t tasklet_irq_handler(int irq,void * devid)
{
	static int count=0;

	printk("Enter tasklet_irq_handler!\n");
	printk("ISR devid:%d\n",(int)(*(int *)devid));
	count++;
	printk("count=%d\n",count);
	if(count > 65536)
	      count=0;
	if(!(count%10)){
	      printk("Every 10 twices enter tasklet bottom half!\n");
	      tasklet_schedule(&slam_tl);
	}
	printk("Leaving IRQ handler!\n");
	return IRQ_HANDLED;
}

static int __init tasklet_irq_init(void)
{
	printk("Enter tasklet_irq_init...\n");
	if(request_irq(irqn,tasklet_irq_handler,IRQF_SHARED,devname,&irqn) != 0)
	{
		printk("Request IRQ failed!\ndevname=%s,IRQ:%d\n",devname,irqn);
		return -1;
	}
	tasklet_init(&slam_tl,slam_tl_handler,0);

	printk("Request IRQ success!\ndevname=%s,IRQ:%d\n",devname,irqn);
        printk("Exit tasklet_irq_init...\n");
	return 0;
}

static void __exit tasklet_irq_exit(void)
{
	printk("Enter tasklet_irq_exit...\n");
	tasklet_kill(&slam_tl);
	free_irq(irqn,&irqn);
	printk("Release irq sucess!\ndevname=%s,IRQ:%d\n",devname,irqn);
	printk("Exit tasklet_irq_exit...\n");
}

module_init(tasklet_irq_init);
module_exit(tasklet_irq_exit);

MODULE_LICENSE("GPL");
